﻿// MIT License Copyright 2014 (c) David Melendez. All rights reserved. See License.txt in the project root for license information.
using ElCamino.AspNet.Identity.AzureTable.Model;
using Microsoft.AspNet.Identity;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Table.Queryable;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using ElCamino.AspNet.Identity.AzureTable.Helpers;

namespace ElCamino.AspNet.Identity.AzureTable
{
    public class UserStore<TUser> : UserStore<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUserStore<TUser>, IUserStore<TUser, string>, IDisposable where TUser : IdentityUser, new()
    {
        public UserStore()
            : this(new IdentityCloudContext<TUser>())
        {
            base.DisposeContext = true;
        }

        public UserStore(IdentityCloudContext<TUser> context)
            : base(context)
        {
        }
    }

    public class UserStore<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : IUserLoginStore<TUser, TKey>
        , IUserClaimStore<TUser, TKey>
        , IUserRoleStore<TUser, TKey>, IUserPasswordStore<TUser, TKey>
        , IUserSecurityStampStore<TUser, TKey>, IQueryableUserStore<TUser, TKey>
        , IUserEmailStore<TUser, TKey>, IUserPhoneNumberStore<TUser, TKey>
        , IUserTwoFactorStore<TUser, TKey>
        , IUserLockoutStore<TUser, TKey>
        , IUserStore<TUser, TKey>
        , IDisposable
        where TUser : IdentityUser<TKey, TUserLogin, TUserRole, TUserClaim>, new()
        where TRole : IdentityRole<TKey, TUserRole>, new()
        where TKey : IEquatable<TKey>
        where TUserLogin : IdentityUserLogin<TKey>, new()
        where TUserRole : IdentityUserRole<TKey>, new()
        where TUserClaim : IdentityUserClaim<TKey>, new()
    {
        private bool _disposed;
        private IQueryable<TUser> _users;
        private IQueryable<IdentityUserIndex> _indexes;

        private CloudTable _userTable;
        private CloudTable _indexTable;

        public UserStore(IdentityCloudContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            this.Context = context;
            this._userTable = context.UserTable;
            this._indexTable = context.IndexTable;
            this._users = _userTable.CreateQuery<TUser>();
            this._indexes = _indexTable.CreateQuery<IdentityUserIndex>();
        }

        public async Task CreateTablesIfNotExists()
        {
            await new TaskFactory().StartNew(() =>
            {
                Task<bool>[] tasks = new Task<bool>[] 
                    { 
                        Context.RoleTable.CreateIfNotExistsAsync(),
                        Context.UserTable.CreateIfNotExistsAsync(),
                        Context.IndexTable.CreateIfNotExistsAsync(),
                    };
                Task.WaitAll(tasks);
            });
        }

        public virtual Task AddClaimAsync(TUser user, Claim claim)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (claim == null)
            {
                throw new ArgumentNullException("claim");
            }
            return new TaskFactory().StartNew(() =>
                {
                    TUserClaim item = Activator.CreateInstance<TUserClaim>();
                    item.UserId = user.Id;
                    item.ClaimType = claim.Type;
                    item.ClaimValue = claim.Value;
                    ((IGenerateKeys)item).GenerateKeys();
                    
                    user.Claims.Add(item);

                    TableResult tresult = _userTable.Execute(TableOperation.Insert(item));
                });
        }

        public virtual Task AddLoginAsync(TUser user, UserLoginInfo login)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (login == null)
            {
                throw new ArgumentNullException("login");
            }
            return new TaskFactory().StartNew(() =>
            {
                TUserLogin item = Activator.CreateInstance<TUserLogin>();
                item.UserId = user.Id;
                item.ProviderKey = login.ProviderKey;
                item.LoginProvider = login.LoginProvider;
                var t = item as IGenerateKeys;
                ((IGenerateKeys)item).GenerateKeys();

                user.Logins.Add(item);

                TableResult tresult = _userTable.Execute(TableOperation.Insert(item));

                IdentityUserIndex index = new IdentityUserIndex()
                {
                    Id = item.UserId.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByLogin(item.LoginProvider),
                    RowKey = item.RowKey,
                    ETag = Constants.ETagWildcard
                };

                TableResult resultIndex = _indexTable.Execute(TableOperation.InsertOrReplace(index));

            });

        }

        public virtual Task AddToRoleAsync(TUser user, string roleName)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (string.IsNullOrWhiteSpace(roleName))
            {
                throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName");
            }

            return new TaskFactory().StartNew(() =>
                {
                    TRole roleT = Activator.CreateInstance<TRole>();
                    roleT.Name = roleName;
                    ((IGenerateKeys)roleT).GenerateKeys();

                    TUserRole userToRole = Activator.CreateInstance<TUserRole>();
                    userToRole.UserId = user.Id;
                    userToRole.RoleId = roleT.Id;
                    userToRole.RoleName = roleT.Name;
                    TUserRole item = userToRole;

                    ((IGenerateKeys)item).GenerateKeys();

                    user.Roles.Add(item);
                    roleT.Users.Add(item);

                    TableResult tresult = _userTable.Execute(TableOperation.Insert(item)); 
                });

        }

        public async virtual Task CreateAsync(TUser user)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            ((IGenerateKeys)user).GenerateKeys();

            await new TaskFactory().StartNew(() =>
            {
                var tresult = _userTable.Execute(TableOperation.Insert(user));

                IdentityUserIndex index = new IdentityUserIndex()
                {
                    Id = user.Id.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByEmail(user.Email),
                    RowKey = KeyHelper.GenerateRowKeyUserEmail(user.Email),
                    ETag = Constants.ETagWildcard
                };

                TableResult resultIndex = _indexTable.Execute(TableOperation.InsertOrReplace(index));

            });

        }

        public async virtual Task DeleteAsync(TUser user)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            await new TaskFactory().StartNew(() =>
            {
                TableBatchOperation userBatch = new TableBatchOperation();

                userBatch.Add(TableOperation.Delete(user));
                foreach (var userLogin in user.Logins)
                {
                    userBatch.Add(TableOperation.Delete(userLogin));

                    IdentityUserIndex indexLogin = new IdentityUserIndex()
                    {
                        Id = user.Id.ToString(),
                        PartitionKey = KeyHelper.GeneratePartitionKeyIndexByLogin(userLogin.LoginProvider),
                        RowKey = userLogin.RowKey,
                        ETag = Constants.ETagWildcard
                    };

                    var resultLoginIndex = _indexTable.Execute(TableOperation.Delete(indexLogin));
                }

                foreach (var userRole in user.Roles)
                {
                    userBatch.Add(TableOperation.Delete(userRole));
                }

                foreach (var userClaim in user.Claims)
                {
                    userBatch.Add(TableOperation.Delete(userClaim));
                }

                var tresult = _userTable.ExecuteBatch(userBatch);

                IdentityUserIndex indexEmail = new IdentityUserIndex()
                {
                    Id = user.Id.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByEmail(user.Email),
                    RowKey = KeyHelper.GenerateRowKeyUserEmail(user.Email),
                    ETag = Constants.ETagWildcard
                };


                var resultEmailIndex = _indexTable.Execute(TableOperation.Delete(indexEmail));

            });

        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if ((this.DisposeContext && disposing) && (this.Context != null))
            {
                this.Context.Dispose();
            }
            this._disposed = true;
            this.Context = null;
            this._userTable = null;
        }

        public async virtual Task<TUser> FindAsync(UserLoginInfo login)
        {
            ThrowIfDisposed();
            if (login == null)
            {
                throw new ArgumentNullException("login");
            }

            string rowKey = login.GenerateRowKeyUserLoginInfo();
            string partitionKey = KeyHelper.GeneratePartitionKeyIndexByLogin(login.LoginProvider);
            var loginQuery = (from l in _indexes
                                where l.RowKey == rowKey
                                where l.PartitionKey == partitionKey
                                select l).AsTableQuery();

            return await GetUserAggregateAsync(loginQuery);
        }

        public Task<TUser> FindByEmailAsync(string email)
        {
            this.ThrowIfDisposed();
            var query = (from entity in _indexTable.CreateQuery<IdentityUserIndex>()
                         where entity.RowKey == KeyHelper.GenerateRowKeyUserEmail(email)
                         where entity.PartitionKey  == KeyHelper.GeneratePartitionKeyIndexByEmail(email)
                         select entity).Take(1).AsTableQuery();

            return this.GetUserAggregateAsync(query);
        }

        public virtual Task<TUser> FindByIdAsync(TKey userId)
        {
            this.ThrowIfDisposed();
            return this.GetUserAggregateAsync(userId.ToString());
        }

        public virtual Task<TUser> FindByNameAsync(string userName)
        {
            this.ThrowIfDisposed();
            return this.GetUserAggregateAsync(KeyHelper.GenerateRowKeyUserName(userName));
        }

        public Task<int> GetAccessFailedCountAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<int>(user.AccessFailedCount);
        }

        public virtual Task<IList<Claim>> GetClaimsAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<IList<Claim>>(user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList());
        }

        public Task<string> GetEmailAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<string>(user.Email);
        }

        public Task<bool> GetEmailConfirmedAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<bool>(user.EmailConfirmed);
        }

        public Task<bool> GetLockoutEnabledAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<bool>(user.LockoutEnabled);
        }

        public Task<DateTimeOffset> GetLockoutEndDateAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<DateTimeOffset>(user.LockoutEndDateUtc.HasValue ? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEndDateUtc.Value, DateTimeKind.Utc)) : new DateTimeOffset());
        }

        public virtual Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<IList<UserLoginInfo>>((from l in user.Logins select new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList<UserLoginInfo>());
        }

        public Task<string> GetPasswordHashAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<string>(user.PasswordHash);
        }

        public Task<string> GetPhoneNumberAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<string>(user.PhoneNumber);
        }

        public Task<bool> GetPhoneNumberConfirmedAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<bool>(user.PhoneNumberConfirmed);
        }

        public virtual Task<IList<string>> GetRolesAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }

            return Task.FromResult<IList<string>>(user.Roles.ToList().Select(r => r.RoleName).ToList());
        }

        public Task<string> GetSecurityStampAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<string>(user.SecurityStamp);
        }

        public Task<bool> GetTwoFactorEnabledAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            return Task.FromResult<bool>(user.TwoFactorEnabled);
        }

        private Task<TUser> GetUserAggregateAsync(string userId)
        {
            return Task.FromResult<TUser>(GetUserAggregate(userId));
        }

        private TUser GetUserAggregate(string userId)
        {
            TUser user = default(TUser);
            var userResults = _userTable.ExecuteQuery(
                    (from d in _userTable.CreateQuery<DynamicTableEntity>()
                     where d.PartitionKey.Equals(userId)
                     select d).AsTableQuery()).ToList();
            var vUser = userResults.Where(u => u.RowKey.Equals(userId) && u.PartitionKey.Equals(userId)).SingleOrDefault();
            var op = new OperationContext();

            if (vUser != null)
            {
                //User
                user = Activator.CreateInstance<TUser>();
                user.ReadEntity(vUser.Properties, op);
                user.PartitionKey = vUser.PartitionKey;
                user.RowKey = vUser.RowKey;
                user.ETag = vUser.ETag;
                user.Timestamp = vUser.Timestamp;

                Task[] tasks = new Task[]
                    { 
                        new TaskFactory().StartNew(()=>
                        {
                            //Roles
                            userResults.Where(u => u.RowKey.StartsWith(Constants.RowKeyConstants.PreFixIdentityUserRole))
                                .ToList()
                                .ForEach(log =>
                                {
                                    TUserRole trole = Activator.CreateInstance<TUserRole>();
                                    trole.ReadEntity(log.Properties, op);
                                    trole.PartitionKey = log.PartitionKey;
                                    trole.RowKey = log.RowKey;
                                    trole.ETag = log.ETag;
                                    trole.Timestamp = log.Timestamp;
                                    user.Roles.Add(trole);
                                });
                        }),
                        new TaskFactory().StartNew(()=>
                        {
                            //Claims
                            userResults.Where(u => u.RowKey.StartsWith(Constants.RowKeyConstants.PreFixIdentityUserClaim))
                                .ToList()
                                .ForEach(log =>
                                {
                                    TUserClaim tclaim = Activator.CreateInstance<TUserClaim>();
                                    tclaim.ReadEntity(log.Properties, op);
                                    tclaim.PartitionKey = log.PartitionKey;
                                    tclaim.RowKey = log.RowKey;
                                    tclaim.ETag = log.ETag;
                                    tclaim.Timestamp = log.Timestamp;
                                    user.Claims.Add(tclaim);
                                });
                            }),
                        new TaskFactory().StartNew(()=>
                        {
                            //Logins
                            userResults.Where(u => u.RowKey.StartsWith(Constants.RowKeyConstants.PreFixIdentityUserLogin))
                                .ToList()
                                .ForEach(log =>
                                {
                                    TUserLogin tlogin = Activator.CreateInstance<TUserLogin>();
                                    tlogin.ReadEntity(log.Properties, op);
                                    tlogin.PartitionKey = log.PartitionKey;
                                    tlogin.RowKey = log.RowKey;
                                    tlogin.ETag = log.ETag;
                                    tlogin.Timestamp = log.Timestamp;
                                    user.Logins.Add(tlogin);
                                });
                         })
                    };
                Task.WaitAll(tasks);
            }
            return user;
        }

        private Task<TUser> GetUserAggregateAsync(TableQuery<IdentityUserIndex> queryUser)
        {
            return new TaskFactory<TUser>().StartNew(() =>
            {
                var user = _indexTable.ExecuteQuery<IdentityUserIndex>(queryUser).SingleOrDefault();
                if (user != null)
                {
                    return GetUserAggregate(user.Id);
                }
             
                return default(TUser);
            });
        }

        public Task<bool> HasPasswordAsync(TUser user)
        {
            return Task.FromResult<bool>(user.PasswordHash != null);
        }

        public Task<int> IncrementAccessFailedCountAsync(TUser user)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.AccessFailedCount++;
            return Task.FromResult<int>(user.AccessFailedCount);
        }

        public virtual Task<bool> IsInRoleAsync(TUser user, string roleName)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (string.IsNullOrWhiteSpace(roleName))
            {
                throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName");
            }

            var query = (from ur in _userTable.CreateQuery<DynamicTableEntity>()
                         where ur.RowKey == KeyHelper.GenerateRowKeyIdentityRole(roleName)
                         where ur.PartitionKey.Equals(user.Id)
                         select ur).Take(1).AsTableQuery();
            return Task.FromResult<bool>(_userTable.ExecuteQuery<DynamicTableEntity>(query).Any());
        }

        public virtual Task RemoveClaimAsync(TUser user, Claim claim)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (claim == null)
            {
                throw new ArgumentNullException("claim");
            }

            if (string.IsNullOrWhiteSpace(claim.Type))
            {
                throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "claim.Type");
            }

            if (string.IsNullOrWhiteSpace(claim.Value))
            {
                throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "claim.Value");
            }

            return new TaskFactory().StartNew(() =>
                {
                    TableBatchOperation batch = new TableBatchOperation();

                    foreach (TUserClaim local in (from uc in user.Claims
                                                  where uc.RowKey == KeyHelper.GenerateRowKeyIdentityUserClaim(claim.Type, claim.Value)
                                                  select uc).ToList<TUserClaim>())
                    {
                        user.Claims.Remove(local);
                        batch.Add(TableOperation.Delete(local));
                    }

                    if (batch.Count > 0)
                    {
                        var tresult = _userTable.ExecuteBatch(batch);
                    }

                });

        }

        public virtual Task RemoveFromRoleAsync(TUser user, string roleName)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (string.IsNullOrWhiteSpace(roleName))
            {
                throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName");
            }
            return new TaskFactory().StartNew(() =>
            {
                TUserRole item = user.Roles.FirstOrDefault<TUserRole>(r => r.RoleName.ToUpper() == roleName.ToUpper());
                if (item != null)
                {
                    user.Roles.Remove(item);
                    TableOperation deleteOperation = TableOperation.Delete(item);

                    TableResult tresult = _userTable.Execute(deleteOperation); 
                }

            });
        }

        public virtual Task RemoveLoginAsync(TUser user, UserLoginInfo login)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (login == null)
            {
                throw new ArgumentNullException("login");
            }
            string provider = login.LoginProvider;
            string key = login.ProviderKey;
            TUserLogin item = user.Logins.SingleOrDefault<TUserLogin>(l => (l.LoginProvider == provider) && (l.ProviderKey == key));
            if (item != null)
            {
                user.Logins.Remove(item);
                TableOperation deleteOperation = TableOperation.Delete(item);

                TableResult tresult = _userTable.Execute(deleteOperation);
                IdentityUserIndex index = new IdentityUserIndex()
                {
                    Id = item.UserId.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByLogin(item.LoginProvider),
                    RowKey = item.RowKey,
                    ETag = Constants.ETagWildcard
                };

                TableResult resultIndex = _indexTable.Execute(TableOperation.Delete(index));
            }
            return Task.FromResult<int>(0);
        }

        public Task ResetAccessFailedCountAsync(TUser user)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.AccessFailedCount = 0;
            return Task.FromResult<int>(0);
        }

        public Task SetEmailAsync(TUser user, string email)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }

            //Only remove the email if different
            //The UserManager calls UpdateAsync which will generate the new email index record
            if (user.Email != email)
            {
                IdentityUserIndex indexEmail = new IdentityUserIndex()
                {
                    Id = user.Id.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByEmail(user.Email),
                    RowKey = KeyHelper.GenerateRowKeyUserEmail(user.Email),
                    ETag = Constants.ETagWildcard
                };

                var tresult = _indexTable.Execute(TableOperation.Delete(indexEmail));

            }
            user.Email = email;
            return Task.FromResult<int>(0);
        }

        public Task SetEmailConfirmedAsync(TUser user, bool confirmed)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.EmailConfirmed = confirmed;
            return Task.FromResult<int>(0);
        }

        public Task SetLockoutEnabledAsync(TUser user, bool enabled)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.LockoutEnabled = enabled;
            return Task.FromResult<int>(0);
        }

        public Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.LockoutEndDateUtc = (lockoutEnd == DateTimeOffset.MinValue) ? null : new DateTime?(lockoutEnd.UtcDateTime);
            return Task.FromResult<int>(0);
        }

        public Task SetPasswordHashAsync(TUser user, string passwordHash)
        {
            this.ThrowIfDisposed();
            
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.PasswordHash = passwordHash;
            return Task.FromResult<int>(0);
        }

        public Task SetPhoneNumberAsync(TUser user, string phoneNumber)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.PhoneNumber = phoneNumber;
            return Task.FromResult<int>(0);
        }

        public Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.PhoneNumberConfirmed = confirmed;
            return Task.FromResult<int>(0);
        }

        public Task SetSecurityStampAsync(TUser user, string stamp)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.SecurityStamp = stamp;
            return Task.FromResult<int>(0);
        }

        public Task SetTwoFactorEnabledAsync(TUser user, bool enabled)
        {
            this.ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            user.TwoFactorEnabled = enabled;
            return Task.FromResult<int>(0);
        }

        private void ThrowIfDisposed()
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(base.GetType().Name);
            }
        }

        public async virtual Task UpdateAsync(TUser user)
        {
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            await new TaskFactory().StartNew(() =>
            {
                var tresult = _userTable.Execute(TableOperation.Replace(user));

                IdentityUserIndex indexEmail = new IdentityUserIndex()
                {
                    Id = user.Id.ToString(),
                    PartitionKey = KeyHelper.GeneratePartitionKeyIndexByEmail(user.Email),
                    RowKey = KeyHelper.GenerateRowKeyUserEmail(user.Email),
                    ETag = Constants.ETagWildcard
                };

                var resultIndex = _indexTable.Execute(TableOperation.InsertOrReplace(indexEmail));

            });
        }

        public IdentityCloudContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> Context { get; private set; }

        public bool DisposeContext { get; set; }

        public IQueryable<TUser> Users
        {
            get
            {
                return _users;
            }
        }
    }
}
